
#include <cmath>
#include <iterator>
#include <omp.h>
#include "config.h"
#include "path_tracing_renderer.h"
#include "ray_buffer.h"
#include "photon_map.h"

#include "film.h"
#include "camera.h"
#include "random_number_generator_mt19937.h"
#include "opencl_device.h"
#include "scene_data.h"
#include "path_tracing.h"
#include <ctime>

PathTracingRenderer::PathTracingRenderer(Camera* c,Film* im,Sampler* s)
{
	camera_ = c;
	image_ = im;
	sampler_ = s;
	device_ = new OpenCLDevice(CL_DEVICE_TYPE_GPU);
	device_->SetKernelFile("path_tracing_kernel.cl", "render");
}
void PathTracingRenderer::InitializeDeviceData(const scene_info_memory_t &scene_info)
{

	device_->SetReadOnlyArg(0,scene_info.lghts);
	device_->SetReadOnlyArg(1,scene_info.material_data);
	device_->SetReadOnlyArg(2,scene_info.shape_data);
	device_->SetReadOnlyArg(3,scene_info.texture_data);
	device_->SetReadOnlyArg(4,scene_info.integrator_data);
	device_->SetReadOnlyArg(5,scene_info.accelerator_data);
	device_->SetReadOnlyArg(6,scene_info.primitives);
	device_->SetReadOnlyArg(7,scene_info.lghts);
	device_->SetReadOnlyArg(11,(unsigned int)scene_info.primitives.size());
	device_->SetReadOnlyArg(12,(unsigned int)scene_info.lghts.size());
}
void PathTracingRenderer::Render(const scene_info_memory_t& scene_info_mem)
{
	RandomNumberGeneratorMT19937 *rng = new RandomNumberGeneratorMT19937(31);
	scene_info_memory_t scene_info = scene_info_mem;
	unsigned int max_depth = 5;
	unsigned int rr_depth = 3;
	scene_info.integrator_data.push_back(as_float(max_depth));
	scene_info.integrator_data.push_back(as_float(rr_depth));

	InitializeDeviceData(scene_info);
	const int buffer_size = 1024*1024*4;
	std::vector<Seed> seeds;
	Seed seed;
	for (int i = 0;i < buffer_size; ++i)
	{
		init_rng(rng->RandomUnsignedInt(),&seed);
		seeds.push_back(seed);
	}

	device_->SetReadWriteArg(9,seeds);
	int iteration = 0;
	while(true)
	{
		clock_t t0 = clock();

		sampler_->ResetSamplePosition();	
		int ray_traced = 0;
		bool has_more_sample = true;

		std::vector<spectrum_t> local_color_buffer;
		std::vector<spectrum_t> readed_color_buffer;
		std::vector<camera_sample_t> local_samples;//all sample per path
		std::vector<camera_sample_t> previous_local_samples;
		//
		std::vector<ray_differential_t> ray_buffer;

		ray_buffer.reserve(buffer_size);
		local_samples.reserve(buffer_size);
		local_color_buffer.reserve(buffer_size);
		previous_local_samples.reserve(buffer_size);
		bool can_read_buffer = false;
		bool is_buffer_readed = false;
		while(has_more_sample)//do eye pass
		{

			camera_sample_t sample;
			ray_differential_t ray;
			float ray_weight;
			ray_buffer.clear();
			local_samples.clear();
			local_color_buffer.clear();

			while(ray_buffer.size() < buffer_size && has_more_sample)
			{
				if(sampler_->GetNextSample(&sample))
				{
					camera_->GenerateRay(sample, &ray, &ray_weight);

					ray_buffer.push_back(ray);
					local_samples.push_back(sample);
				}
				else
					has_more_sample = false;
			}
			local_color_buffer.resize(ray_buffer.size(),spectrum_t());
		
#define USE_OPENCL
#ifndef USE_OPENCL
			for(size_t i = 0;i < ray_buffer.size(); ++i)
			{
				cl_scene_info_t cl_scene_info = as_cl_scene_info(scene_info);
				path_tracing(cl_scene_info.light_data,cl_scene_info.material_data,cl_scene_info.shape_data,
				cl_scene_info.texture_data,cl_scene_info.integrator_data,cl_scene_info.accelerator_data,
				cl_scene_info.primitives,cl_scene_info.primitive_count,cl_scene_info.lghts,cl_scene_info.lght_count,
				&ray_buffer[i],&seeds[i],&local_color_buffer[i]);
				image_->AddSample(local_samples[i],local_color_buffer[i]);
			}
#else
			if(!ray_buffer.empty())
			{
				//try to read buffer
				if(can_read_buffer)
				{
					device_->ReadBuffer(10,&readed_color_buffer[0],readed_color_buffer.size());
					is_buffer_readed = true;
				}
				device_->SetReadOnlyArg(8,ray_buffer);
				device_->SetReadWriteArg(10,local_color_buffer);
				device_->SetReadOnlyArg(13,(unsigned int)local_color_buffer.size());
				device_->Run(ray_buffer.size());
				if(is_buffer_readed)
				{
					for(size_t i = 0;i < previous_local_samples.size(); ++i)
						image_->AddSample(previous_local_samples[i],readed_color_buffer[i]);
					ray_traced += ray_buffer.size();
					printf("\r%d ray traced... time_costs: %lf",ray_traced,(clock()-t0)/double(CLOCKS_PER_SEC));
					is_buffer_readed = false;
				}
				readed_color_buffer.resize(local_color_buffer.size(),spectrum_t());
				previous_local_samples.swap(local_samples);
				can_read_buffer = true;
			}
			//last pass
			if(can_read_buffer && !is_buffer_readed)
			{
				device_->ReadBuffer(10,&readed_color_buffer[0],readed_color_buffer.size());
				is_buffer_readed = true;
			}
			if(is_buffer_readed)
			{
				for(size_t i = 0;i < previous_local_samples.size(); ++i)
					image_->AddSample(previous_local_samples[i],readed_color_buffer[i]);
				ray_traced += ray_buffer.size();
				printf("\r%d ray traced... time_costs: %lf",ray_traced,(clock()-t0)/double(CLOCKS_PER_SEC));
				is_buffer_readed = false;
			}
#endif
		}
		if(iteration % 2 == 0)
			image_->WriteImage(iteration);
		iteration++;
	}
	delete rng;
}
PathTracingRenderer::~PathTracingRenderer()
{
	delete camera_;
	delete image_;
	delete sampler_;
	delete device_;
}